一次线上内存泄漏的解决过程

您所在的位置:网站首页 nginx 内存 无限增长 一次线上内存泄漏的解决过程

一次线上内存泄漏的解决过程

2023-04-17 22:08| 来源: 网络整理| 查看: 265

内存泄漏这种问题是可遇不可求的经历,终于有机会抓住了它,要好好的记录下来。出现问题的是打成jar包的一个引擎程序

引擎逻辑

大致是生产者消费者模式的一个数据处理引擎

public class MainClass { public static void main(String[] args) { try { //定义 线程池、队列、门闩 ExecutorService service = Executors.newCachedThreadPool(); BlockingQueue queue = new LinkedBlockingQueue(100); CountDownLatch latch = new CountDownLatch(10); //1个生产者 Producer producer = new Producer(queue); service.execute(producer); //10个消费者,每个消费者加门闩,消费完成减一 for (int i = 0; i < 10; i++) { service.submit(new Consumer(queue,latch)); } service.shutdown(); //主线程等待门闩,都完成后开始第二次循环 try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } }catch (Exception e){ } try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("循环一次结束,第二次开始调用"); main(new String[]{}); } }

业务逻辑为生产者消费者启动,用CountDownLatch来阻塞住主线程,等所有消费者生产者线程完成并结束后,main方法开始调用自己,开始第二次启动,循环调用

这种情况下运行一段时间后会出现异常:

Caused by: java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:717) at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:957) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1367) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)

针对OutOfMemoryError异常我们使用jdk自带的工具jvisualvm来查看

jvisualvm使用

jvisualvm自从 JDK 6 Update 7 以后已经作为JDK 的一部分,位于 JDK 根目录的 bin 文件夹下,无需安装,直接运行即可

image.png 打开后左侧是所有的进程,可以打开任意一个进行详细信息查看 image.png 右侧对应显示详细信息 image.png

分析程序崩溃时堆文件

程序运行时,设置参数

-Xms200m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/dump/

设置最大内存和指定OutOfMemoryError时存储堆文件的位置

我们使用jvisualvm打开堆文件java_pid42132.hprof

image.png

占用内存最大的是Obeject[]和byte[],并没有显示具体是哪个类导致的内存问题,暂时无从下手。

猜想1:线程池的线程数过多导致

我们只能从程序逻辑来猜想这个问题了,由于程序多次回调,很有可能是线程池里的线程未及时关闭导致的,我们修改代码来验证

public class MainClass { //全局线程池 static ExecutorService service = Executors.newCachedThreadPool(); public static void main(String[] args) { try { //定义 线程池、队列、门闩 BlockingQueue queue = new LinkedBlockingQueue(100); CountDownLatch latch = new CountDownLatch(10); //1个生产者 Producer producer = new Producer(queue); service.execute(producer); //10个消费者,每个消费者加门闩,消费完成减一 for (int i = 0; i < 10; i++) { service.submit(new Consumer(queue,latch)); } service.shutdown(); //主线程等待门闩,都完成后开始第二次循环 try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } //输出线程池状态 System.out.println(service.toString()); }catch (Exception e){ } try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("循环一次结束,第二次开始调用"); main(new String[]{}); } }

定义全局的线程池变量,每次输出线程池状态【长度,活动线程数,完成线程数】

java.util.concurrent.ThreadPoolExecutor@720653c2[Running, pool size = 11, active threads = 0, queued tasks = 0, completed tasks = 11] 循环一次结束,第二次开始调用 java.util.concurrent.ThreadPoolExecutor@720653c2[Running, pool size = 11, active threads = 0, queued tasks = 0, completed tasks = 22] 循环一次结束,第二次开始调用 java.util.concurrent.ThreadPoolExecutor@720653c2[Running, pool size = 11, active threads = 0, queued tasks = 0, completed tasks = 33] 循环一次结束,第二次开始调用 java.util.concurrent.ThreadPoolExecutor@720653c2[Running, pool size = 11, active threads = 0, queued tasks = 0, completed tasks = 44] 循环一次结束,第二次开始调用 java.util.concurrent.ThreadPoolExecutor@720653c2[Running, pool size = 11, active threads = 0, queued tasks = 0, completed tasks = 55] 循环一次结束,第二次开始调用 java.util.concurrent.ThreadPoolExecutor@720653c2[Running, pool size = 11, active threads = 0, queued tasks = 0, completed tasks = 66] 循环一次结束,第二次开始调用

通过输出可以看到:

存活线程数一直是0,当前线程池长度为pool size=11,也就是刚执行完的来不及释放的1个生产者10个消费者线程,已完成线程数completed tasks=11,22,33,44,55,66... 依次增长。

排除了线程池带来的内存溢出。

main方法无限回调导致的内存问题

为了验证这个猜想,设计代码如下

public class MainClass { public static void main(String[] args) { try { //定义 线程池、队列、门闩 ExecutorService service = Executors.newCachedThreadPool(); BlockingQueue queue = new LinkedBlockingQueue(100); CountDownLatch latch = new CountDownLatch(10); //new 10个生产者 for(int i=0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3